﻿using System;
using System.Collections.Generic;
using System.Text;
using RoughJs.Ast;

namespace RoughJs.E4X
{
    public class QName : IdScriptableObject
    {
        private const int
       Id_constructor = 1,
       Id_toString = 2,
       Id_toSource = 3,
       MAX_PROTOTYPE_ID = 3;


        private static Object QNAME_TAG = "QName";

        private XMLLibImpl lib;

        private QName prototype;

        private XmlNode.QName delegate1;

        private QName()
        {
        }

       public static QName create(XMLLibImpl lib, Scriptable scope, QName prototype, XmlNode.QName delegate1)
        {
            QName rv = new QName();
            rv.lib = lib;
            rv.setParentScope(scope);
            rv.prototype = prototype;
            rv.setPrototype(prototype);
            rv.delegate1 = delegate1;
            return rv;
        }


      public  void exportAsJSClass(bool sealed1)
        {
            exportAsJSClass(MAX_PROTOTYPE_ID, getParentScope(), sealed1);
        }


        public override String ToString()
        {
            //    ECMA357 13.3.4.2
            if (delegate1.getNamespace() == null)
            {
                return "*::" + localName();
            }
            else if (delegate1.getNamespace().isGlobal())
            {

                return localName();
            }
            else
            {
                return uri() + "::" + localName();
            }
        }

        public String localName()
        {
            if (delegate1.getLocalName() == null) return "*";
            return delegate1.getLocalName();
        }

   
        public String prefix()
        {
            if (delegate1.getNamespace() == null) return null;
            return delegate1.getNamespace().getPrefix();
        }

        public String uri()
        {
            if (delegate1.getNamespace() == null) return null;
            return delegate1.getNamespace().getUri();
        }


        public XmlNode.QName toNodeQname()
        {
            return delegate1;
        }

       public XmlNode.QName getDelegate()
        {
            return delegate1;
        }


        public override bool Equals(Object obj)
        {
            if (!(obj is QName)) return false;
            return equals((QName)obj);
        }


        public override Object equivalentValues(Object value)
        {
            if (!(value is QName)) return ScriptableObject.NOT_FOUND;
            bool result = equals((QName)value);
            return result ? true : false;
        }

        private bool equals(QName q)
        {
            return this.delegate1.isEqualTo(q.delegate1);
        }


        public override String getClassName()
        {
            return "QName";
        }


        public Object getDefaultValue(Type hint)
        {
            return ToString();
        }

        // #string_id_map#
        private const int
            Id_localName = 1,
            Id_uri = 2,
            MAX_INSTANCE_ID = 2;


        public int getMaxInstanceId()
        {
            return base.getMaxInstanceId() + MAX_INSTANCE_ID;
        }


        public override int findInstanceIdInfo(String s)
        {
            int id;
            id = 0; String X = null;
            int s_length = s.Length;
            if (s_length == 3) { X = "uri"; id = Id_uri; }
            else if (s_length == 9) { X = "localName"; id = Id_localName; }
            if (X != null && X != s && !(X == s)) id = 0;
            goto L0;
        L0: ;


            if (id == 0) return base.findInstanceIdInfo(s);

            int attr;
            switch (id)
            {
                case Id_localName:
                case Id_uri:
                    attr = PERMANENT | READONLY;
                    break;
                default: throw new Exception();
            }
            return instanceIdInfo(attr, base.getMaxInstanceId() + id);
        }


        public override String getInstanceIdName(int id)
        {
            switch (id - base.getMaxInstanceId())
            {
                case Id_localName: return "localName";
                case Id_uri: return "uri";
            }
            return base.getInstanceIdName(id);
        }


        public override Object getInstanceIdValue(int id)
        {
            switch (id - base.getMaxInstanceId())
            {
                case Id_localName: return localName();
                case Id_uri: return uri();
            }
            return base.getInstanceIdValue(id);
        }




        public override int findPrototypeId(String s)
        {
            int id;

            id = 0; String X = null; int c;
            int s_length = s.Length;
            if (s_length == 8)
            {
                c = s.ToCharArray()[3];
                if (c == 'o') { X = "toSource"; id = Id_toSource; }
                else if (c == 't') { X = "toString"; id = Id_toString; }
            }
            else if (s_length == 11) { X = "constructor"; id = Id_constructor; }
            if (X != null && X != s && !(X == s)) id = 0;
            goto L0;

        L0: ;

            return id;
        }



        public override void initPrototypeId(int id)
        {
            String s;
            int arity;
            switch (id)
            {
                case Id_constructor: arity = 2; s = "constructor"; break;
                case Id_toString: arity = 0; s = "toString"; break;
                case Id_toSource: arity = 0; s = "toSource"; break;
                default: throw new ArgumentException(id.ToString());
            }
            initPrototypeMethod(QNAME_TAG, id, s, arity);
        }


        public override Object execIdCall(IdFunctionObject f,
                                 Context cx,
                                 Scriptable scope,
                                 Scriptable thisObj,
                                 Object[] args)
        {
            if (!f.hasTag(QNAME_TAG))
            {
                return base.execIdCall(f, cx, scope, thisObj, args);
            }
            int id = f.methodId();
            switch (id)
            {
                case Id_constructor:
                    return jsConstructor(cx, (thisObj == null), args);
                case Id_toString:
                    return realThis(thisObj, f).ToString();
                case Id_toSource:
                    return realThis(thisObj, f).js_toSource();
            }
            throw new ArgumentException(id.ToString());
        }

        private QName realThis(Scriptable thisObj, IdFunctionObject f)
        {
            if (!(thisObj is QName))
                throw incompatibleCallError(f);
            return (QName)thisObj;
        }

        public QName newQName(XMLLibImpl lib, String q_uri, String q_localName, String q_prefix)
        {
            QName prototype = this.prototype;
            if (prototype == null)
            {
                prototype = this;
            }
            XmlNode.Namespace ns = null;
            if (q_prefix != null)
            {
                ns = XmlNode.Namespace.create(q_uri, q_prefix);
            }
            else if (q_uri != null)
            {
                ns = XmlNode.Namespace.create(q_uri);
            }
            else
            {
                ns = null;
            }
            if (q_localName != null && (q_localName == "*")) q_localName = null;
            return create(lib, this.getParentScope(), prototype, XmlNode.QName.create(ns, q_localName));
        }

        //    See ECMA357 13.3.2
        public QName constructQName(XMLLibImpl lib, Context cx, Object namespace1, Object name)
        {
            String nameString = null;
            if (name is QName)
            {
                if (namespace1 == Undefined.instance)
                {
                    return (QName)name;
                }
                else
                {
                    nameString = ((QName)name).localName();
                }
            }
            if (name == Undefined.instance)
            {
                nameString = "";
            }
            else
            {
                nameString = ScriptRuntime.toString(name);
            }

            if (namespace1 == Undefined.instance)
            {
                if ("*" == nameString)
                {
                    namespace1 = null;
                }
                else
                {
                    namespace1 = lib.getDefaultNamespace(cx);
                }
            }
            Namespace namespaceNamespace = null;
            if (namespace1 == null)
            {
                //    leave as null
            }
            else if (namespace1 is Namespace)
            {
                namespaceNamespace = (Namespace)namespace1;
            }
            else
            {
                namespaceNamespace = lib.newNamespace(ScriptRuntime.toString(namespace1));
            }
            String q_localName = nameString;
            String q_uri;
            String q_prefix;
            if (namespace1 == null)
            {
                q_uri = null;
                q_prefix = null;    //    corresponds to undefined; see QName class
            }
            else
            {
                q_uri = namespaceNamespace.uri();
                q_prefix = namespaceNamespace.prefix();
            }
            return newQName(lib, q_uri, q_localName, q_prefix);
        }

        public QName constructQName(XMLLibImpl lib, Context cx, Object nameValue)
        {
            return constructQName(lib, cx, Undefined.instance, nameValue);
        }

        public QName castToQName(XMLLibImpl lib, Context cx, Object qnameValue)
        {
            if (qnameValue is QName)
            {
                return (QName)qnameValue;
            }
            return constructQName(lib, cx, qnameValue);
        }

        private Object jsConstructor(Context cx, bool inNewExpr, Object[] args)
        {
            //    See ECMA357 13.3.2
            if (!inNewExpr && args.Length == 1)
            {
                return castToQName(lib, cx, args[0]);
            }
            if (args.Length == 0)
            {
                return constructQName(lib, cx, Undefined.instance);
            }
            else if (args.Length == 1)
            {
                return constructQName(lib, cx, args[0]);
            }
            else
            {
                return constructQName(lib, cx, args[0], args[1]);
            }
        }

        private String js_toSource()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append('(');
            toSourceImpl(uri(), localName(), prefix(), sb);
            sb.Append(')');
            return sb.ToString();
        }

        private static void toSourceImpl(String uri, String localName, String prefix, StringBuilder sb)
        {
            sb.Append("new QName(");
            if (uri == null && prefix == null)
            {
                if (!("*" == localName))
                {
                    sb.Append("null, ");
                }
            }
            else
            {
                Namespace.toSourceImpl(prefix, uri, sb);
                sb.Append(", ");
            }
            sb.Append('\'');
            sb.Append(ScriptRuntime.escapeString(localName, '\''));
            sb.Append("')");
        }
    }// end for class
}
